package net.imagine.backend.mockrmi.remote.server;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import net.imagine.backend.mockrmi.ServiceRegistry;
import net.imagine.backend.mockrmi.remote.protocol.RequestBean;
import net.imagine.backend.mockrmi.remote.protocol.ResponseBean;
import net.imagine.backend.mockrmi.util.MockRmiUtils;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;

/**
 * 服务器端用于处理用户请求的类
 * 
 * @author wuqh
 */
public class RequestProcessor {
	protected static final Log logger = LogFactory.getLog(RequestProcessor.class);

	// private final int port;
	//
	// public RequestProcessor(int port) {
	// this.port = port;
	// }

	/**
	 * 处理服务器接收到的客户端请求
	 * 
	 * @param request
	 *            客户端请求的封装类
	 * @param port
	 *            服务器的端口号
	 * 
	 * @return 服务器处理结果的封装对象
	 */
	public static ResponseBean processRequest(RequestBean request, int port) {
		String id = request.getId();
		String methodName = request.getMethodName();
		List<Object> params = request.getParams();
		Object target = ServiceRegistry.lookup(MockRmiUtils.genId(port, id));
		Assert.notNull(target, "id(" + MockRmiUtils.genId(port, id) + ") is not registed in server");
		Method method = null;
		method = findPublicMethod(target, methodName, getParameterTypes(params));
		if (method == null) {
			NoSuchMethodException e = new NoSuchMethodException(methodToString(target, methodName, params));
			logger.warn("NoSuchMethodException occurred", e);
			return MockRmiUtils.packResponse(id, e);
		}

		method.setAccessible(true);
		Object ret = null;
		try {
			ret = method.invoke(target, getArgs(params));
		} catch (IllegalArgumentException e) {
			logger.warn("IllegalArgumentException occurred", e);
			return MockRmiUtils.packResponse(id, e);
		} catch (IllegalAccessException e) {
			logger.warn("IllegalAccessException occurred", e);
			return MockRmiUtils.packResponse(id, e);
		} catch (InvocationTargetException e) {
			logger.warn("InvocationTargetException occurred", e);
			return MockRmiUtils.packResponse(id, e);
		}
		if (ret == null && !method.getReturnType().equals(Void.TYPE)) {
			if (logger.isDebugEnabled()) {
				logger.debug("not void method " + methodToString(target, methodName, params) + " but return null");
			}
		}
		if (logger.isDebugEnabled()) {
			logger.debug("invoke method " + methodToString(target, methodName, params) + "with return " + ret);
		}
		return MockRmiUtils.packResponse(id, ret);
	}

	/**
	 * 通过参数获取参数类型
	 * 
	 * @param params
	 *            参数列表
	 * @return 参数对应的类型
	 */
	private static Class<?>[] getParameterTypes(List<Object> params) {
		int size = params.size();
		Class<?>[] parameterTypes = new Class<?>[size];
		for (int i = 0; i < size; i++) {
			parameterTypes[i] = params.get(i).getClass();
		}
		return parameterTypes;
	}

	/**
	 * 根据方法名和参数类型获取公共方法（由于是接口，方法一定是公共的）。 由于方法中定义的可能是接口，而传入的参数类型是实现类，需要做一定处理
	 * 
	 * @param target
	 *            执行方法的对象
	 * @param methodName
	 *            方法名
	 * @param parameterTypes
	 *            参数类型
	 * 
	 * @return 匹配的方法
	 */
	private static Method findPublicMethod(Object target, String methodName, Class<?>... parameterTypes) {
		Method[] methods = target.getClass().getMethods();
		for (Method method : methods) {
			if (method.getName().equals(methodName)) {
				if (isMethodMatch(method, parameterTypes)) {
					return method;
				}
			} else {
				continue;
			}
		}
		return null;
	}

	/**
	 * 判断方法和参数是否匹配
	 * 
	 * @param method
	 *            方法
	 * @param parameterTypes
	 *            参数类型
	 * 
	 * @return 方法和参数是否匹配
	 */
	private static boolean isMethodMatch(Method method, Class<?>... parameterTypes) {
		Class<?>[] methodParamTypes = method.getParameterTypes();
		int methodParamsLen = methodParamTypes.length;
		if (methodParamsLen != parameterTypes.length) {
			return false;
		}
		for (int i = 0; i < methodParamsLen; i++) {
			// 如果方法中定义的类必须是传入参数的接口或者超类
			if (!methodParamTypes[i].isAssignableFrom(parameterTypes[i])) {
				return false;
			}
		}
		return true;
	}

	/**
	 * 把参数列表转换为数组
	 * 
	 * @param params
	 *            参数列表
	 * @return 参数数组
	 */
	private static Object[] getArgs(List<Object> params) {
		int size = params.size();
		Object[] args = new Object[size];
		for (int i = 0; i < size; i++) {
			args[i] = params.get(i);
		}
		return args;
	}

	private static String methodToString(Object target, String methodName, List<Object> params) {
		StringBuilder returnStr = new StringBuilder();
		returnStr.append(target.getClass().getName());
		returnStr.append('.');
		returnStr.append(methodName);
		returnStr.append('(');
		for (Object param : params) {
			returnStr.append(param.getClass().getName());
			returnStr.append(", ");
		}
		returnStr.substring(0, returnStr.length() - 2);
		returnStr.append(')');
		return returnStr.toString();
	}

	public static void main(String... args) {
		List<Object> params = new ArrayList<Object>();
		params.add("123");
		params.add(true);
		System.out.println(methodToString(new Object(), "test", params));
		System.out.println(String.class.isAssignableFrom(String.class));
	}
}
